package ffte.misc

import spinal.core._
import spinal.lib._

/**
 * Synchronous FIFO component for FFT data buffering
 *
 * This class implements a synchronous FIFO (First-In-First-Out) buffer
 * for streaming data in FFT implementations. It provides efficient
 * data buffering with configurable depth and type parameterization.
 *
 * Key Features:
 * - Generic data type support for any SpinalHDL Data type
 * - Configurable buffer size with automatic address width calculation
 * - Push/pop stream interface with backpressure support
 * - Flush capability for FIFO reset and clearing
 * - Efficient memory implementation using single-port RAM
 *
 * Architecture:
 * - Circular buffer using dual pointers (push/pop)
 * - Synchronous read/write operations
 * - Full/empty status detection for flow control
 * - Single-cycle flush operation for immediate reset
 *
 * Applications:
 * - Data buffering between FFT pipeline stages
 * - Rate matching in streaming implementations
 * - Temporal storage for data reordering operations
 * - Handling of variable-latency processing
 *
 * @param itype HardType specifying the data type to store
 * @param size FIFO depth (number of elements)
 * @tparam T Data type parameter (constrained to Data)
 */
class syncfifo[T <: Data](itype:HardType[T],size:Int) extends Component {

    /** FIFO interface with streaming push/pop and flush control */
    val io = new Bundle {
        /** Input stream interface (slave) */
        val push = slave(Stream(itype()))

        /** Output stream interface (master) */
        val pop  = master(Stream(itype()))

        /** Flush control to immediately reset FIFO */
        val flush = in Bool
    }

    /** Address width for pointer calculations */
    val sW       = log2Up(size)

    /** Internal RAM storage for FIFO data */
    val ram      = Mem(itype, size)

    /** Push pointer (write address) */
    val pushPtr  = RegInit(U(0,sW bits))

    /** Pop pointer (read address) */
    val popPtr   = RegInit(U(0,sW bits))

    /** Next push pointer value */
    val pushNext = UInt(sW bits)

    /** Incremented push pointer for full detection */
    val pushInc  = UInt(sW bits)

    /** Next pop pointer value */
    val popNext  = UInt(sW bits)

    /** Push operation active flag */
    val pushing  = io.push.fire

    /** Pop operation active flag */
    val poping   = io.pop.fire

    /** FIFO empty status (pointers equal) */
    val empty    = pushPtr === popPtr

    /** FIFO full status (next write would overwrite read) */
    val full     = RegNext(pushInc === popPtr,False)

    // Update pointers with next values
    pushPtr     := pushNext
    popPtr      := popNext

    // Flow control signals
    io.push.ready  := !full
    io.pop.valid   := !empty

    // Synchronous read from RAM
    io.pop.payload := ram.readSync(
        address = popNext
    )

    // Synchronous write to RAM
    ram.write(
        address = pushPtr,
        data    = io.push.payload,
        enable  = pushing
    )

    // Initialize next pointers with current values
    pushNext := pushPtr
    popNext  := popPtr

    // Pointer management logic
    when(io.flush){
        // Flush: reset both pointers to zero
        pushNext := U(0)
        popNext  := U(0)
    }.otherwise {
        // Normal operation: increment pointers on valid operations
        when(pushing) {
            when(pushPtr===size-1) {
                pushNext := U(0)  // Wrap around
            }.otherwise {
                pushNext := pushPtr+1
            }
        }
        when(poping) {
            when(popPtr===size-1) {
                popNext := U(0)   // Wrap around
            }.otherwise {
                popNext := popPtr+1
            }
        }
    }

    // Calculate next push pointer for full detection
    pushInc := Mux(pushPtr===size-1, U(0), (pushPtr+1))
}